package io.github.jesse205.transferstation.lite.managers;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Display;
import android.view.DragEvent;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.webkit.MimeTypeMap;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.android.documentsui.DragShadowBuilder;
import com.jesse205.util.FileUtil;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Objects;

import io.github.jesse205.transferstation.lite.DragDropApplication;
import io.github.jesse205.transferstation.lite.IconResources;
import io.github.jesse205.transferstation.lite.R;
import io.github.jesse205.transferstation.lite.components.CardView;
import io.github.jesse205.transferstation.lite.databinding.PanelLayoutBinding;
// import io.github.jesse205.transferstation.lite.helpers.ClipDataHelper;
import io.github.jesse205.transferstation.lite.helpers.ThemeHelper;
import io.github.jesse205.transferstation.lite.services.DragDropService;
import io.github.jesse205.transferstation.lite.sources.PanelSource;

/**
 * 拖放面板管理器
 */
public class DragPanelManager implements View.OnDragListener, View.OnTouchListener, View.OnLongClickListener, View.OnClickListener, PopupMenu.OnMenuItemClickListener {
    private static final String TAG = "DragPanelManager";
    private static final float TENSION_ANIM = 0.8f;
    private static final float FACTOR_BADGE = 3f;
    private static final int TIME_BADGE_SCALE = 200;
    private static final int TIME_CONTENT_ALPHA = 300;
    private static final int DRAG_AND_DROP_FLAG = View.DRAG_FLAG_OPAQUE | View.DRAG_FLAG_GLOBAL
            | View.DRAG_FLAG_GLOBAL_URI_READ | View.DRAG_FLAG_GLOBAL_URI_WRITE;
    private final Activity activity;
    private final DragDropApplication application;
    private final DragDropService.DragAndDropServiceBinder service;
    private final WindowManager windowManager;
    private final WindowManager.LayoutParams layoutParams;

    private final Handler handler = new Handler();
    private final PanelSource panelSource;
    private final int moveThreshold;
    private final int hiddeWidth;
    private final ViewsHolder[] viewsHolders;
    @NonNull
    private final DragDropManager dragDropManager;
    private final PropertyChangeListener onDragDropManagerChangeListener = this::onDragDropManagerChanged;
    private int initTouchX;
    private int initTouchY;
    private int initX;
    private int initY;
    private int positionX;
    private int positionY;
    private int screenWidth;
    private int screenHeight;
    private boolean isClosing;
    private boolean isHiding = false;
    private boolean isSelfDrag = false;
    private boolean isFocusing = false;
    private boolean adsorb = false;
    private boolean isPreInSystemWindow = false;
    private boolean isInSystemWindow = false;
    @Nullable
    private Animator xAnimator;
    private final Runnable hidePanelRunnable = this::hideDragPanel;
    @Nullable
    private ValueAnimator yAnimator;

    @SuppressLint("ClickableViewAccessibility")
    public DragPanelManager(Activity activity, ViewsHolder[] viewsHolders, PanelSource panelSource) {
        this.activity = activity;
        this.panelSource = panelSource;
        this.viewsHolders = viewsHolders;

        application = (DragDropApplication) activity.getApplicationContext();
        service = application.getAppService();
        dragDropManager = DragDropManager.getInstance(activity);
        dragDropManager.addListener(onDragDropManagerChangeListener);

        for (ViewsHolder holder : viewsHolders) {
            holder.panelRippleView.removeAllViews();
            holder.panelRippleView.addView(holder.panelFileContentView);

            holder.rootView.setOnClickListener(this);// 主要用来点击展开和恢复悬浮窗显示
            holder.panelRippleView.setOnClickListener(this);
            holder.panelRippleView.setOnLongClickListener(this);
            holder.rootView.setOnTouchListener(this);
            holder.panelRippleView.setOnTouchListener(this);
            holder.panelView.setOnDragListener(this);
            // holder.rootView.setOnDragListener(this);
            // if (holder.canAddToWindow)
            //     holder.rootView.setOnApplyWindowInsetsListener(this);
            // holder.rootView.setFitsSystemWindows(true);

            holder.panelRippleView.setTag(holder);
            holder.panelView.setTag(holder);
            holder.rootView.setTag(holder);
            holder.badgeView.setScaleX(0);
            holder.badgeView.setScaleY(0);


        }

        moveThreshold = activity.getResources().getDimensionPixelSize(R.dimen.move_threshold);
        hiddeWidth = activity.getResources().getDimensionPixelSize(R.dimen.hidden_width);
        isClosing = !panelSource.getState();

        windowManager = (WindowManager) application.getSystemService(Context.WINDOW_SERVICE);
        layoutParams = new WindowManager.LayoutParams();
        initLayoutParams(layoutParams);
        refreshWindowBound(application.getResources().getConfiguration());
        /* DisplayMetrics metrics = new DisplayMetrics();

        windowManager.getDefaultDisplay().getRealMetrics(metrics); */

       /*  screenWidth = metrics.widthPixels;
        screenHeight = metrics.heightPixels;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            windowInsets = windowManager.getMaximumWindowMetrics().getWindowInsets();
        } */
        // new WindowInsets.CONSUMED;
        // windowManager.getDefaultDisplay().get;

        // Point screenPoint = new Point();
        // windowManager.getDefaultDisplay().getSize(screenPoint);
        // screenWidth = screenPoint.x;
        // screenHeight = screenPoint.y;
        if (dragDropManager.hasData())
            changePanelContent(dragDropManager.getItem(0));
        else
            changePanelContent(null);
    }

    private void initLayoutParams(WindowManager.LayoutParams layoutParams) {
        layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        layoutParams.gravity = Gravity.LEFT | Gravity.TOP;

        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
        layoutParams.format = PixelFormat.RGBA_8888;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
        }
    }
    // region 窗口添加与删除

    /**
     * 将面板添加到窗口
     */
    public void addPanelToWindow() {
        Log.d(TAG, "addPanelToWindow: ");
        if (isPreInSystemWindow)
            return;
        isPreInSystemWindow = true;
        if (isInSystemWindow)
            forceRemovePanelFromWindow();
        layoutParams.alpha = 0;
        layoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
        for (ViewsHolder holder : viewsHolders) {
            if (holder.canAddToWindow) {
                windowManager.addView(holder.rootView, layoutParams);
                isInSystemWindow = true;
                holder.rootView.post(() -> {
                    isClosing = false;
                    setPosition(holder, application.getPanelX(), application.getPanelY());
                    layoutParams.x = getClosedX(holder);
                    updateRootLayout(holder);
                    isClosing = !panelSource.getState();
                    if (!isClosing)
                        openDragPanel();
                });
            }
        }
    }

    /**
     * 将面板移除窗口
     */
    public void removePanelFromWindow() {
        Log.d(TAG, "removePanelFromWindow: ");
        // 如果面板没有关闭，则先关闭再移除
        if (!isPreInSystemWindow)
            return;
        isPreInSystemWindow = false;
        // if (!service.isFinishing())
        //     panelSource.setState(false);
        for (ViewsHolder holder : viewsHolders) {
            if (holder.canAddToWindow) {
                // 判断一下,当 view 已经关闭时不再播放动画
                if (layoutParams.x != getClosedX(holder)) {
                    closeDragPanel();
                    if (xAnimator != null)
                        xAnimator.addListener(new AnimatorListenerAdapter() {
                            @Override
                            public void onAnimationEnd(@NonNull Animator animation) {
                                super.onAnimationEnd(animation);
                                forceRemovePanelFromWindow();
                            }
                        });
                } else {
                    forceRemovePanelFromWindow();
                }
            }
        }
    }

    public void forceRemovePanelFromWindow() {
        Log.d(TAG, "forceRemovePanelFromWindow: ");
        if (!isInSystemWindow)
            return;
        isInSystemWindow = false;
        if (xAnimator != null) {
            xAnimator.removeAllListeners();
            xAnimator.cancel();
        }
        for (ViewsHolder holder : viewsHolders) {
            if (holder.canAddToWindow) {
                if (holder.rootView.getParent() != null) {
                    windowManager.removeView(holder.rootView);
                }
            }
        }
    }
    // endregion

    // region 面板打开关闭与隐藏

    /**
     * 打开面板
     */
    public void openDragPanel() {
        blockClosingAndOpening(true);
        if (!isPreInSystemWindow)
            return;

        layoutParams.alpha = 1;
        layoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
        for (ViewsHolder holder : viewsHolders) {
            updateRootLayout(holder);
            refreshBadge(holder);
        }
        application.setPanelPosition(positionX, positionY);
        animateToPositionX();
        animateToPositionY();
    }

    /**
     * 关闭面板
     */
    public void closeDragPanel() {
        blockClosingAndOpening(false);
        xAnimator = new AnimatorSet();
        ArrayList<Animator> animators = new ArrayList<>();

        for (ViewsHolder holder : viewsHolders) {
            if (holder.canAddToWindow) {
                ValueAnimator animator = ValueAnimator.ofInt(layoutParams.x, getClosedX(holder));
                animator.setInterpolator(new OvershootInterpolator(TENSION_ANIM));
                animator.setDuration(500);
                animator.addUpdateListener(animation -> {
                    layoutParams.x = (int) animation.getAnimatedValue();
                    updateRootLayout(holder);
                });
                animator.addListener(new AnimatorListenerAdapter() {
                    // 安卓在动画取消之后仍然会调用onAnimationCancel，因此需要先做个标记，判断是否已取消
                    private boolean isCanceled = false;

                    @Override
                    public void onAnimationCancel(Animator animation) {
                        isCanceled = true;
                    }

                    @Override
                    public void onAnimationEnd(Animator animation) {
                        // 防止因为提前结束导致的直接消失的问题
                        if (isCanceled)
                            return;
                        layoutParams.alpha = 0;
                        layoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
                        updateRootLayout(holder);

                    }
                });
                animators.add(animator);
            }
        }
        ((AnimatorSet) xAnimator).playTogether(animators);
        xAnimator.start();

    }


    /**
     * 隐藏面板
     */
    public void hideDragPanel() {
        if (xAnimator != null)
            xAnimator.cancel();
        isHiding = true;
        xAnimator = new AnimatorSet();
        ArrayList<Animator> animators = new ArrayList<>();

        for (ViewsHolder holder : viewsHolders) {
            if (holder.canAddToWindow) {
                refreshBadge(holder);
                ValueAnimator animator = ValueAnimator.ofInt(layoutParams.x, getHiddenX(holder));
                animator.setInterpolator(new OvershootInterpolator(TENSION_ANIM));
                animator.setDuration(500);
                animator.addUpdateListener(animation -> {
                    layoutParams.x = (int) animation.getAnimatedValue();
                    updateRootLayout(holder);
                });
                animators.add(animator);
            }

        }
        ((AnimatorSet) xAnimator).playTogether(animators);
        xAnimator.start();
    }

    private void blockClosingAndOpening(boolean nextState) {
        if (xAnimator != null)
            xAnimator.cancel();
        if (!service.isFinishing() && isPreInSystemWindow)
            panelSource.setState(nextState);
        isClosing = !nextState;
        isHiding = !nextState;
    }

    public void animateToPositionX() {
        if (xAnimator != null)
            xAnimator.cancel();
        if (layoutParams.x != positionX) {
            xAnimator = ValueAnimator.ofInt(layoutParams.x, positionX);
            xAnimator.setInterpolator(new OvershootInterpolator(TENSION_ANIM));
            xAnimator.setDuration(500);
            ((ValueAnimator) xAnimator).addUpdateListener(animation -> {
                for (ViewsHolder holder : viewsHolders) {
                    setPositionX(holder, (int) animation.getAnimatedValue());
                    updateRootLayout(holder);
                }

            });
            xAnimator.start();
        }
    }

    public void animateToPositionY() {
        if (yAnimator != null)
            yAnimator.cancel();
        if (layoutParams.y != positionY) {
            yAnimator = ValueAnimator.ofInt(layoutParams.y, positionY);
            yAnimator.setInterpolator(new OvershootInterpolator(TENSION_ANIM));
            yAnimator.setDuration(500);
            yAnimator.addUpdateListener(animation -> {
                for (ViewsHolder holder : viewsHolders) {
                    setPositionY(holder, (int) animation.getAnimatedValue());
                    updateRootLayout(holder);
                }
            });
            yAnimator.start();
        }
    }

    // endregion

    // region 面板聚焦与取消聚焦

    public void focusDragPanel(ViewsHolder holder) {
        isFocusing = true;
        if (holder.scaleAnimator != null)
            holder.scaleAnimator.cancel();
        ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(holder.panelView, "scaleX", 1.05f);
        ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(holder.panelView, "scaleY", 1.05f);
        scaleXAnimator.setAutoCancel(true);
        scaleYAnimator.setAutoCancel(true);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.setDuration(200);
        animatorSet.playTogether(scaleXAnimator, scaleYAnimator);
        animatorSet.start();
        holder.scaleAnimator = animatorSet;
        holder.panelRippleView.setHovered(true);
        // 关闭角标
        refreshBadge(holder);

    }

    public void unfocusDragPanel(ViewsHolder holder) {
        isFocusing = false;
        if (holder.scaleAnimator != null)
            holder.scaleAnimator.cancel();
        ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(holder.panelView, "scaleX", 1);
        ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(holder.panelView, "scaleY", 1);
        scaleXAnimator.setAutoCancel(true);
        scaleYAnimator.setAutoCancel(true);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.setDuration(500);
        animatorSet.setInterpolator(new OvershootInterpolator(TENSION_ANIM));
        animatorSet.playTogether(scaleXAnimator, scaleYAnimator);
        animatorSet.start();
        holder.scaleAnimator = animatorSet;

        holder.panelRippleView.setHovered(false);
        refreshBadge(holder);
    }
    // endregion

    /**
     * @return 获取上下文
     */
    @NonNull
    public Activity getActivity() {
        return activity;
    }

    // region 位置设置与归位
    public void setPositionX(ViewsHolder holder, int x) {
        if (!isInSystemWindow || !holder.canAddToWindow)
            return;
        int layoutWidth = holder.rootView.getWidth();
        int maxPositionX = screenWidth - layoutWidth;
        int minPositionX = 0;
        layoutParams.x = x;
        // 设置面板位置
        holder.position = screenWidth / 2 > x + layoutWidth / 2 ? PanelPosition.LEFT : PanelPosition.RIGHT;
        // 禁止坐标超出屏幕
        // if (!isClosing && !isHiding)
            if (adsorb) {
                positionX = holder.position == PanelPosition.LEFT ? minPositionX : maxPositionX;
            } else
                positionX = Math.max(minPositionX, Math.min(x, maxPositionX));
    }

    public void setPositionY(ViewsHolder holder, int y) {
        if (!isInSystemWindow || !holder.canAddToWindow)
            return;
        int layoutHeight = holder.rootView.getHeight();
        int maxPositionY = screenHeight - layoutHeight;
        int minPositionY = 0;
        layoutParams.y = y;
        // 禁止坐标超出屏幕
        // if (!isClosing)
            positionY = Math.max(minPositionY, Math.min(y, maxPositionY));
    }

    public void setPosition(ViewsHolder holder, int x, int y) {
        setPositionX(holder, x);
        setPositionY(holder, y);
    }
    // endregion

    private int getClosedX(ViewsHolder holder) {
        if (!isInSystemWindow)
            return 0;
        return holder.position == PanelPosition.LEFT ? -holder.rootView.getWidth() : screenWidth;
    }

    private int getHiddenX(ViewsHolder holder) {
        if (!isInSystemWindow)
            return 0;
        return getClosedX(holder) + (holder.position == PanelPosition.LEFT ? hiddeWidth : -hiddeWidth);
    }

    /**
     * 更新根布局，用于在悬浮窗位置改变后刷新位置
     *
     * @param holder view 持有者
     */
    private void updateRootLayout(@NonNull ViewsHolder holder) {
        if (!isInSystemWindow || !holder.canAddToWindow)
            return;
        windowManager.updateViewLayout(holder.rootView, layoutParams);
        holder.rootView.invalidate();
        holder.panelView.invalidate();
        holder.panelView.invalidateOutline();
    }


    public boolean isClosing() {
        return isClosing;
    }

    public boolean isAdsorb() {
        return adsorb;
    }

    public void setAdsorb(boolean adsorb) {
        this.adsorb = adsorb;
        if (adsorb) {
            for (ViewsHolder holder : viewsHolders) {
                if (holder.canAddToWindow) {
                    int layoutWidth = holder.rootView.getWidth();
                    int maxPositionX = screenWidth - layoutWidth;
                    int nowX = layoutParams.x;
                    positionX = holder.position == PanelPosition.LEFT ? 0 : maxPositionX;
                    if (!isClosing && (nowX != 0 || nowX != maxPositionX))
                        holder.panelView.post(this::openDragPanel);

                }
            }
        }
    }


    /**
     * @param v      用于启用拖拽的控件
     * @param holder 数据持有者
     * @return 是否成功启动了拖拽
     */
    public boolean startDragAndDrop(View v, @NonNull DragDropManager.DataHolder holder) {
        if (activity.isFinishing() || holder.type == DragDropManager.DataType.LOADING)
            return false;
        ClipData clipData=holder.clipData;
        DragShadowBuilder dragShadowBuilder = new DragShadowBuilder(activity);
        @Nullable String title = holder.title;
        int iconColor = 0;
        int icon = 0;
        switch (holder.type) {
            case TEXT: {
                iconColor = IconResources.COLOR_FILE_TEXT;
                icon = IconResources.ICON_TEXT;
                break;
            }
            case FILE: {
                if (title != null) {
                    String extensionName = FileUtil.getExtensionName(title);
                    if (extensionName != null) {
                        String mimeType = MimeTypeMap.getSingleton()
                                .getMimeTypeFromExtension(extensionName);
                        if (mimeType != null) {
                            icon = IconResources.getFileIcon(mimeType);
                            iconColor = IconResources.getFileIconColor(activity, mimeType);
                        } else {
                            icon = IconResources.ICON_FILE;
                        }
                    }
                }
                break;
            }
        }

        if (title == null)
            title = activity.getString(R.string.unknown);
        if (icon == 0)
            icon = R.drawable.baseline_question_mark_24;
        if (iconColor == 0)
            iconColor = getThemeColor(android.R.attr.textColorSecondary);

        dragShadowBuilder.updateIcon(icon);
        dragShadowBuilder.updateIconColor(iconColor);
        dragShadowBuilder.updateTitle(title);
        if (clipData != null) {
            dragShadowBuilder.updateBagde(clipData.getItemCount());
            // ClipDataHelper.grantAdditionalAppsPermissions(activity, clipData);
        } else
            dragShadowBuilder.updateBagde(0);
        isSelfDrag = true;
        v.startDragAndDrop(clipData, dragShadowBuilder, null, DRAG_AND_DROP_FLAG);
        return true;
    }

    public void refreshWindowBound(@NonNull Configuration newConfig) {
        screenWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, newConfig.screenWidthDp, application.getResources().getDisplayMetrics());
        screenHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, newConfig.screenHeightDp, application.getResources().getDisplayMetrics());

        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics metrics = new DisplayMetrics();
        display.getRealMetrics(metrics);
        screenWidth = metrics.widthPixels;
        screenHeight = metrics.heightPixels;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // windowInsets = windowManager.getMaximumWindowMetrics().getWindowInsets();
            // Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars());
            Rect bounds = windowManager.getMaximumWindowMetrics().getBounds();
            screenWidth = bounds.width();
            screenHeight = bounds.height();
        }
    }

    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        refreshWindowBound(newConfig);

        for (ViewsHolder holder : viewsHolders) {
            if (holder.canAddToWindow) {
                if (adsorb)
                    setPosition(holder, holder.position == PanelPosition.LEFT ? 0 : screenWidth, positionY);
                else
                    setPosition(holder, positionX, positionY);
                setPosition(holder, positionX, positionY);
                if (isClosing)
                    setPositionX(holder, getClosedX(holder));
                updateRootLayout(holder);
            }
        }
        /* if (!isClosing) {
            animateToPositionY();
            animateToPositionX();
        } */
    }

    public void onDragDropManagerChanged(PropertyChangeEvent evt) {
        activity.runOnUiThread(() -> {
            if (dragDropManager.hasData()) {
                DragDropManager.DataHolder holder = dragDropManager.getItem(0);
                changePanelContentWithAnimation(holder);
            } else {
                changePanelContentWithAnimation(null);
            }
        });

    }

    public void onDestroy() {
        dragDropManager.removeListener(onDragDropManagerChangeListener);
        removePanelFromWindow();
    }

    public void changePanelContentWithAnimation(@NonNull ViewsHolder holder,
                                                @Nullable DragDropManager.DataHolder dataHolder) {
        View childView = holder.panelRippleView.getChildAt(0);
        ObjectAnimator animator = ObjectAnimator.ofFloat(childView, "alpha", 0);
        animator.setAutoCancel(true);
        animator.setDuration(TIME_CONTENT_ALPHA / 2);
        animator.addListener(new AnimatorListenerAdapter() {
            private boolean isCanceled = false;

            @Override
            public void onAnimationCancel(Animator animation) {
                isCanceled = true;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                if (isCanceled)
                    return;
                changePanelContent(holder, dataHolder);
                childView.setAlpha(1);
                View childView = holder.panelRippleView.getChildAt(0);
                ObjectAnimator animator = ObjectAnimator.ofFloat(childView, "alpha", 0, 1);
                animator.setAutoCancel(true);
                animator.setDuration(TIME_CONTENT_ALPHA / 2);
                animator.start();
            }
        });
        animator.start();
    }

    public void changePanelContentWithAnimation(@Nullable DragDropManager.DataHolder dataHolder) {
        for (ViewsHolder holder : viewsHolders) {
            changePanelContentWithAnimation(holder, dataHolder);
        }
    }

    public void changePanelContent(@NonNull ViewsHolder holder, @Nullable DragDropManager.DataHolder dataHolder) {
        TextView textView = null;
        ImageView iconView = null;

        refreshBadge(holder);
        if (dataHolder == null) {
            holder.panelRippleView.removeAllViews();
            holder.panelRippleView.addView(holder.panelFileContentView);
            textView = holder.panelFileContentView.findViewById(R.id.title);
            iconView = holder.panelFileContentView.findViewById(R.id.icon);
            textView.setText(R.string.drop_here);
            iconView.setImageResource(IconResources.ICON_FILE_PLUS);
            iconView.setColorFilter(getThemeColor(android.R.attr.textColorSecondary));
            return;
        }
        String title;
        int icon = 0;
        int iconColor = 0;

        title = dataHolder.title;
        switch (dataHolder.type) {
            case TEXT: {
                holder.panelRippleView.removeAllViews();
                holder.panelRippleView.addView(holder.panelTextContentView);
                textView = holder.panelTextContentView.findViewById(R.id.text);
                break;
            }
            case FILE: {
                holder.panelRippleView.removeAllViews();
                holder.panelRippleView.addView(holder.panelFileContentView);
                textView = holder.panelFileContentView.findViewById(R.id.title);
                iconView = holder.panelFileContentView.findViewById(R.id.icon);
                if (title != null) {
                    String mimeType = MimeTypeMap.getSingleton()
                            .getMimeTypeFromExtension(FileUtil.getExtensionName(title));
                    if (mimeType != null) {
                        icon = IconResources.getFileIcon(mimeType);
                        iconColor = IconResources.getFileIconColor(activity, mimeType);
                    }
                }
                if (icon == 0)
                    icon = IconResources.ICON_FILE;
                break;
            }
            case LOADING:
                holder.panelRippleView.removeAllViews();
                holder.panelRippleView.addView(holder.panelLoadingContentView);
                break;
            default: {
                holder.panelRippleView.removeAllViews();
                holder.panelRippleView.addView(holder.panelFileContentView);
                textView = holder.panelFileContentView.findViewById(R.id.title);
                iconView = holder.panelFileContentView.findViewById(R.id.icon);
                title = activity.getString(R.string.unknown);
            }
        }
        if (textView != null) {
            if (title == null)
                title = activity.getString(R.string.unknown);
            textView.setText(title);
        }
        if (iconView != null) {
            if (icon == 0)
                icon = R.drawable.baseline_question_mark_24;
            if (iconColor == 0)
                iconColor = getThemeColor(android.R.attr.textColorSecondary);
            iconView.setImageResource(icon);
            iconView.setColorFilter(iconColor);
        }

    }

    public void changePanelContent(@Nullable DragDropManager.DataHolder dataHolder) {
        for (ViewsHolder holder : viewsHolders) {
            changePanelContent(holder, dataHolder);
        }
    }


    /**
     * 清除已保存的内容
     */
    public void clearSavedClipData() {
        changePanelContentWithAnimation(null);
        // clipDataSource.setData(null);
        dragDropManager.destroyAllItems();
    }

    /**
     * 获取主题色
     *
     * @param attr attr 地址
     * @return 颜色值
     */
    private int getThemeColor(int attr) {
        return ThemeHelper.getThemeColor(activity, attr);
    }

    /**
     * 设置角标。此方法会自动播放动画。
     *
     * @param num 数量
     */
    private void setBadge(ViewsHolder holder, int num) {
        int endScale = num <= 1 ? 0 : 1;
        TextView badgeView = holder.badgeView;


        if (num > 1) {  // 打开角标
            badgeView.setText(num > 99 ? "∞" : String.valueOf(num));
        }
        if (holder.badgeAnimator != null)
            holder.badgeAnimator.cancel();
        if (badgeView.getScaleX() != endScale) {
            Interpolator interpolator = endScale == 0 ? new AccelerateInterpolator(FACTOR_BADGE) : new DecelerateInterpolator();
            holder.badgeAnimator = ValueAnimator.ofFloat(badgeView.getScaleX(), endScale);
            holder.badgeAnimator.setDuration(TIME_BADGE_SCALE);
            holder.badgeAnimator.setInterpolator(interpolator);
            holder.badgeAnimator.addUpdateListener(animation -> {
                badgeView.setScaleX((Float) animation.getAnimatedValue());
                badgeView.setScaleY((Float) animation.getAnimatedValue());
                // 这里textview的文字可能无法显示，原因未知
                badgeView.invalidate();
            });
            holder.badgeAnimator.start();

        }


        badgeView.invalidate();
    }

    public boolean isPreInSystemWindow() {
        return isPreInSystemWindow;
    }

    public boolean isInSystemWindow() {
        return isInSystemWindow;
    }

    public void showMenu(ViewsHolder holder) {
        if (!holder.canShowMenu)
            return;
        int gravity = holder.position == PanelPosition.LEFT ? Gravity.LEFT : Gravity.RIGHT;
        PopupMenu popupMenu = new PopupMenu(activity, holder.menuAnchor, gravity);
        popupMenu.inflate(R.menu.drag_panel_menu);
        Menu menu = popupMenu.getMenu();
        menu.findItem(R.id.clear_panel_data).setVisible(dragDropManager.getItemCount() > 0);
        popupMenu.setOnMenuItemClickListener(this);
        popupMenu.show();
    }

    @Nullable
    public Animator getXAnimator() {
        return xAnimator;
    }

    // region 重写
    @Override
    public boolean onDrag(View v, DragEvent event) {
        if (activity.isFinishing())
            return false;
        ViewsHolder holder = (ViewsHolder) v.getTag();
        switch (event.getAction()) {
            case DragEvent.ACTION_DRAG_STARTED:
                holder.canShowMenu = true;
                if (isSelfDrag)
                    /* 部分系统开启拖拽后不会立刻触发 ACTION_DRAG_LOCATION，这使得用户可能将内容脱出面板而不触发任何事件，
                    所以延迟 500 毫秒单独判断 */
                    handler.postDelayed(hidePanelRunnable, 500L);
                openDragPanel();
                break;
            case DragEvent.ACTION_DRAG_LOCATION:
                if (isSelfDrag)
                    handler.removeCallbacks(hidePanelRunnable);
                holder.panelRippleView.setHovered(true);
                break;
            case DragEvent.ACTION_DROP:
                unfocusDragPanel(holder);
                if (isSelfDrag) {
                    showMenu(holder);
                    return true;
                }
                // 接下来肯定会加载数据，所以把角标立刻设为 0，防止因取消面板导致的角标闪现
                setBadge(holder, 0);
                ClipData clipData = event.getClipData();
                clearSavedClipData();
                activity.requestDragAndDropPermissions(event);
                dragDropManager.newItem(activity, clipData);
                holder.panelRippleView.drawableHotspotChanged(event.getX(), event.getY());
                holder.panelRippleView.setPressed(true);
                holder.panelRippleView.setPressed(false);
                break;
            case DragEvent.ACTION_DRAG_ENDED:
                if (isSelfDrag) {
                    handler.removeCallbacks(hidePanelRunnable);
                    openDragPanel();
                    isSelfDrag = false;
                }
                if (!dragDropManager.hasData())
                    closeDragPanel();
                holder.canShowMenu = true;
                break;
            case DragEvent.ACTION_DRAG_ENTERED:
                handler.removeCallbacks(hidePanelRunnable);
                focusDragPanel(holder);
                if (isClosing || isHiding)
                    openDragPanel();
                break;
            case DragEvent.ACTION_DRAG_EXITED:
                unfocusDragPanel(holder);
                holder.canShowMenu = false;
                if (isSelfDrag) {
                    hideDragPanel();
                }
                break;
        }
        return true;
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        ViewsHolder holder = (ViewsHolder) v.getTag();

        if (activity.isFinishing())
            return false;
        int rawX = (int) event.getRawX();
        int rawY = (int) event.getRawY();
        int endX, endY;
        int movedX, movedY;


        if (yAnimator != null)
            yAnimator.cancel();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                initTouchX = rawX;
                initTouchY = rawY;
                initX = layoutParams.x;
                initY = layoutParams.y;
                holder.menuAnchor.setTranslationX(event.getX());
                holder.menuAnchor.setTranslationY(event.getY());
                blockClosingAndOpening(true);
                refreshBadge(holder);
                break;
            case MotionEvent.ACTION_MOVE:
                movedX = rawX - initTouchX;
                movedY = rawY - initTouchY;
                if (movedX < -moveThreshold || movedX > moveThreshold
                        || movedY < -moveThreshold || movedY > moveThreshold)
                    v.setPressed(false);
                if (holder.canAddToWindow) {
                    endX = initX + movedX;
                    endY = initY + movedY;
                    setPosition(holder, endX, endY);
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                holder.canMoveY = false;
                if (holder.canAddToWindow) {
                    endX = initX + rawX - initTouchX;
                    application.setPanelPosition(positionX, positionY);
                    int toHideWidth = holder.rootView.getWidth() / 3;
                    if (endX < -toHideWidth || endX > screenWidth - holder.rootView.getWidth() + toHideWidth) {
                        if (dragDropManager.hasData())
                            hideDragPanel();
                        else
                            closeDragPanel();
                    } else
                        openDragPanel();
                    animateToPositionY();
                }
                break;
        }
        if (!holder.canAddToWindow) {
            holder.position = (event.getX() < (int) (v.getWidth() / 2)) ? PanelPosition.LEFT : PanelPosition.RIGHT;
        }

        updateRootLayout(holder);
        return false;
    }

    @Override
    public boolean onLongClick(View v) {
        if (activity.isFinishing())
            return false;
        boolean flag = false;
        if (dragDropManager.hasData()) {
            DragDropManager.DataHolder holder = dragDropManager.getItem(0);
            flag = startDragAndDrop(v, holder);

        }
        if (!flag)
            showMenu((ViewsHolder) v.getTag());
        return true;
    }

    // endregion

    @Override
    public void onClick(View v) {
        if (isHiding)
            openDragPanel();
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        if (item.getItemId() == R.id.close_panel) {
            DragPanelManager.this.closeDragPanel();
            activity.moveTaskToBack(true);
        } else if (item.getItemId() == R.id.clear_panel_data) {
            clearSavedClipData();
        }
        return true;
    }

    public void refreshBadge(ViewsHolder holder) {
        boolean canShowBadge = dragDropManager.hasData()
                && dragDropManager.getItem(0).clipData != null;

        if ((isClosing || isHiding) && holder.canAddToWindow)
            canShowBadge = false;
        if (isFocusing)
            canShowBadge = false;

        if (canShowBadge)
            setBadge(holder, Objects.requireNonNull(dragDropManager.getItem(0).clipData).getItemCount());
        else
            setBadge(holder, 0);
    }


    /**
     * 面板位置。有左和右两种选项
     */
    public enum PanelPosition {
        LEFT, RIGHT
    }

    public static class ViewsHolder {
        public final RelativeLayout rootView;
        public final CardView panelView;
        public final ViewGroup panelRippleView;
        public final TextView badgeView;
        public final View menuAnchor;

        public final View panelTextContentView;
        public final View panelFileContentView;
        public final View panelLoadingContentView;
        public boolean canAddToWindow;
        public boolean canMoveY = false;
        public boolean canShowMenu = true;
        public PanelPosition position = PanelPosition.LEFT;
        // public WindowInsets windowInsets;
        @Nullable
        private ValueAnimator badgeAnimator;
        @Nullable
        private AnimatorSet scaleAnimator;

        public ViewsHolder(PanelLayoutBinding binding, boolean canAddToWindow) {
            this.canAddToWindow = canAddToWindow;
            rootView = binding.getRoot();
            panelView = binding.panelView;
            panelRippleView = binding.panelRipple;
            menuAnchor = binding.menuAnchor;
            badgeView = binding.panelLayoutBadge.getRoot();
            panelTextContentView = rootView.findViewById(R.id.panel_content_text);
            panelFileContentView = rootView.findViewById(R.id.panel_content_file);
            panelLoadingContentView = rootView.findViewById(R.id.panel_content_loading);
            // windowInsets = rootView.getRootWindowInsets();
        }
    }

}
